using System.Collections.Immutable;
using System;
using System.Net;
using Vue_AspNetCore_Project.Data;
using Vue_AspNetCore_Project.Helper;
using Vue_AspNetCore_Project.Models;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.SpaServices;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Tokens;
using VueCliMiddleware;
using Newtonsoft.Json;
using Microsoft.Extensions.FileProviders;
using System.IO;
using Vue_AspNetCore_Project.Services;

namespace Vue_AspNetCore_Project
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {

            services.AddDbContext<ApplicationDbContext>(options =>
               options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

            services.AddIdentity<ApplicationUser, ApplicationRole>(config =>
            {
                config.User.RequireUniqueEmail = true;
                config.Password.RequireDigit = false;
                config.Password.RequiredLength = 6;
                config.Password.RequireLowercase = false;
                config.Password.RequireNonAlphanumeric = false;
                config.Password.RequireUppercase = false;

            }).AddEntityFrameworkStores<ApplicationDbContext>()
                .AddErrorDescriber<CustomIdentityErrorDescriber>()
                .AddDefaultTokenProviders();

            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
            {
                options.RequireHttpsMetadata = false;
                options.SaveToken = true;
                options.ClaimsIssuer = Configuration["Authentication:JwtIssuer"];
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = true,
                    ValidIssuer = Configuration["Authentication:JwtIssuer"],
                    ValidateAudience = true,
                    ValidAudience = Configuration["Authentication:JwtAudience"],
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(Configuration["Authentication:JwtKey"])),
                    RequireExpirationTime = true,
                    ValidateLifetime = true,
                    ClockSkew = TimeSpan.Zero
                };
            }); ;


            // Add application services.
            services.AddTransient<IEmailSender, EmailSender>();
            services.Configure<EmailOptions>(Configuration.GetSection("EmailOptions"));

            services.AddControllersWithViews().AddNewtonsoftJson();

             services.AddDistributedRedisCache(option =>
            {
                option.Configuration = Configuration.GetConnectionString("RedisConnection");
                option.InstanceName = "master";
            });
            
            // Add AddRazorPages if the app uses Razor Pages.
            // services.AddRazorPages();
            // In production, the Vue files will be served from this directory
            services.AddSpaStaticFiles(configuration =>
            {
                configuration.RootPath = "ClientApp/dist";
            });

        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.UseExceptionHandler(Builder => Builder.Run(
                async context =>
                {
                    context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
                    context.Response.ContentType = "application/json";
                    var ex = context.Features.Get<IExceptionHandlerFeature>();

                    if (ex != null)
                    {
                        if (env.IsDevelopment())
                        {
                            var error = new
                            {
                                title = ex.Error.Message,
                                status = context.Response.StatusCode,
                                errors = ex.Error.StackTrace

                            };
                            await context.Response.WriteAsync(JsonConvert.SerializeObject(error)).ConfigureAwait(false);
                        }
                        else
                        {
                            var error = new
                            {
                                title = ex.Error.Message,
                                status = context.Response.StatusCode,

                            };

                            await context.Response.WriteAsync(JsonConvert.SerializeObject(error)).ConfigureAwait(false);
                        }

                    }

                }));


            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            // app.UseHsts();
            app.UseStaticFiles();
            app.UseSpaStaticFiles();
            app.UseCors();

            app.UseRouting();
            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller}/{action=Index}/{id?}");

                if (env.IsDevelopment())
                {
                    endpoints.MapToVueCliProxy(
                   "*",
                   new SpaOptions { SourcePath = "ClientApp" },
                   npmScript: "serve",
                   regex: "Compiled successfully");
                }
                else
                {
                    endpoints.MapFallbackToFile("ClientApp/dist/index.html", new StaticFileOptions
                    {
                        ServeUnknownFileTypes = true,
                        FileProvider = new PhysicalFileProvider(Directory.GetCurrentDirectory())
                    });

                }
            });

        }
    }
}